<!--
 * @Author: your name
 * @Date: 2020-04-30 09:23:07
 * @LastEditTime: 2021-04-02 13:46:04
 * @LastEditors: ding-cx
 * @Description: 基础组件，数据驱动视图的，根据配置生成form表单
 * @FilePath: \ipms\src\components\base\BaseForm.vue
 -->

 <template>
  <el-form
    ref="form"
    class="base-form"
    :class="oc(option, 'hideErrorMsg') ? 'base-form-not-error-msg' : ''"
    :label-width="oc(option, 'labelWidth') || '80px'"
    :label-position="oc(option, 'labelPosition') || 'right'"
    :model="formModel"
    :rules="oc(option, 'rules') || null"
    :size="oc(option, 'size') || 'small'"
  >
    <!-- v-loading="loading"
    element-loading-text="正在提交..."-->
    <el-row :gutter="oc(option, 'gutter') || 0">
      <el-col
        :style="{ clear: item.newRow ? 'both' : 'none' }"
        :key="index"
        v-for="(item, index) in option.items"
        :md="oc(item, 'span') || 6"
        :sm="12"
        :xs="24"
      >
        <transition name="fade-transform">
          <div v-show="oc(item, 'display') !== false">
            <el-form-item
              v-if="!item.table"
              :label="item.label"
              :prop="item.prop"
              :label-width="
                oc(item, 'labelWidth') || oc(option, 'labelWidth') || '80px'
              "
              :rules="item.rules"
            >
              <!-- 终极兜底slot，自定义插槽 -->
              <div v-if="item.slot">
                <slot :name="item.prop" :option="item"></slot>
              </div>
              <!-- 普通表单组件 -->
              <component
                :ref="item.prop"
                v-else-if="
                  item.component !== 'el-checkbox-group' &&
                  item.component !== 'el-radio-group'
                "
                style="width: 100%"
                :is="item.component"
                v-bind="item.componentProps"
                v-model="formModel[item.prop]"
                @change="onChange(item, $event)"
                :value-key="oc(item, 'options.valueKey')"
              >
                <template
                  v-for="slotItem in item.componentSlots"
                  v-slot:[slotItem]
                >
                  <slot
                    :name="item.prop + '-' + slotItem"
                    :option="item"
                  ></slot>
                </template>
                <!-- select 特殊处理 -->
                <template
                  v-if="
                    item.component === 'el-select' && item.display !== false
                  "
                  #default
                >
                  <el-option
                    v-for="(selectItem, selectIndex) in item.options.list"
                    :key="selectIndex"
                    :label="selectItem[item.options.label]"
                    :value="
                      item.options.valueField !== undefined
                        ? selectItem[item.options.valueField]
                        : selectItem
                    "
                    :disabled="selectItem.disabled"
                  >
                    <template
                      v-for="slotItem in item.componentSlots"
                      v-slot:[slotItem]
                    >
                      <slot :name="item.prop + '-' + slotItem"></slot>
                    </template>
                  </el-option>
                </template>
              </component>

              <!-- 特殊表单组件 checkbox -->
              <el-checkbox-group
                :ref="item.prop"
                v-else-if="
                  item.component === 'el-checkbox-group' &&
                  item.display !== false
                "
                v-bind="item.componentProps"
                v-model="formModel[item.prop]"
                @change="onChange(item, $event)"
              >
                <!-- 通过options的label和valueKey决定list列表里谁显示，谁是值。 -->
                <el-checkbox
                  v-for="(checkitem, cindex) in item.options.list"
                  :key="cindex"
                  :label="
                    item.options.valueKey !== undefined
                      ? checkitem[item.options.valueKey]
                      : checkitem
                  "
                  >{{
                    item.options.label !== undefined
                      ? checkitem[item.options.label]
                      : item.options.valueKey !== undefined
                      ? checkitem[item.options.valueKey]
                      : checkitem
                  }}</el-checkbox
                >
              </el-checkbox-group>

              <!-- 特殊表单组件 radio -->
              <el-radio-group
                :ref="item.prop"
                v-else-if="
                  item.component === 'el-radio-group' && item.display !== false
                "
                v-bind="item.componentProps"
                v-model="formModel[item.prop]"
                @change="onChange(item, $event)"
              >
                <!-- 通过options的label和text决定list列表里谁显示，谁是值。 -->
                <el-radio
                  v-for="(checkitem, cindex) in item.options.list"
                  :key="cindex"
                  :label="
                    item.options.valueKey !== undefined
                      ? checkitem[item.options.valueKey]
                      : checkitem
                  "
                  >{{
                    item.options.label !== undefined
                      ? checkitem[item.options.label]
                      : item.options.valueKey !== undefined
                      ? checkitem[item.options.valueKey]
                      : checkitem
                  }}</el-radio
                >
              </el-radio-group>
            </el-form-item>
            <!-- 表格批量表单 -->
            <el-form-item
              v-else
              :label="item.label"
              :prop="item.prop"
              :label-width="
                oc(item, 'labelWidth') || oc(option, 'labelWidth') || '80px'
              "
            >
              <el-table
                :ref="item.prop"
                class="base-form-table"
                style="width: 100%"
                :data="formModel[item.prop]"
              >
                <!-- @row-click="tableRowClick(item,arguments)" -->
                <el-table-column
                  v-if="
                    oc(item, 'tableOption.indexColumn.display') !== false ||
                    false
                  "
                  header-align="center"
                  align="center"
                  prop="index"
                  label="序号"
                  type="index"
                  :index="oc(item, 'tableOption.indexColumn.index') || 1"
                  :width="70"
                ></el-table-column>
                <el-table-column
                  :key="index2"
                  v-for="(item2, index2) in item.columns"
                  :prop="item2.prop"
                  :label="item2.label"
                  align="center"
                  :width="item2.width"
                >
                  <template #default="scope">
                    <component
                      style="width: 100%"
                      :is="item2.component"
                      v-bind="item2.componentProps"
                      v-model="scope.row[item2.prop]"
                      @change="tableListChange(scope)"
                      :size="oc(item2, 'componentProps.size') || 'small'"
                    ></component>
                    <!-- v-model="formModel[item.prop][index2][item2.prop]" -->
                  </template>
                </el-table-column>

                <el-table-column
                  v-if="
                    oc(item, 'tableOption.operationColumn.display') !== false ||
                    false
                  "
                  header-align="center"
                  :width="oc(item, 'tableOption.operationColumn.width')"
                  :fixed="oc(item, 'tableOption.operationColumn.fixed')"
                  align="center"
                  prop="operation"
                  label="操作"
                >
                  <template #default="scope">
                    <!-- v-show="currentTableIndex === scope.$index" -->
                    <el-dropdown
                      v-if="
                        oc(item, 'tableOption.operationColumn.menuType') ===
                        'dropdown'
                      "
                    >
                      <span class="el-dropdown-link">
                        {{
                          oc(
                            item,
                            "tableOption.operationColumn.dropdownText"
                          ) || "菜单"
                        }}
                        <i class="el-icon-arrow-down el-icon--right"></i>
                      </span>
                      <el-dropdown-menu slot="dropdown">
                        <el-dropdown-item>
                          <el-button
                            v-if="
                              oc(
                                item,
                                'tableOption.operationColumn.upMoveBtn'
                              ) !== false
                            "
                            type="text"
                            @click="tableRowUpOne(formModel[item.prop], scope)"
                            :disabled="scope.$index === 0"
                          >
                            <i class="el-icon-top"></i>上移
                          </el-button>
                        </el-dropdown-item>
                        <el-dropdown-item>
                          <el-button
                            v-if="
                              oc(
                                item,
                                'tableOption.operationColumn.downMoveBtn'
                              ) !== false
                            "
                            type="text"
                            @click="
                              tableRowDownOne(formModel[item.prop], scope)
                            "
                            :disabled="
                              scope.$index === formModel[item.prop].length - 1
                            "
                          >
                            <i class="el-icon-bottom"></i>下移
                          </el-button>
                        </el-dropdown-item>
                        <el-dropdown-item>
                          <el-button
                            v-if="
                              oc(
                                item,
                                'tableOption.operationColumn.clearBtn'
                              ) !== false || false
                            "
                            type="text"
                            icon="el-icon-close"
                            @click="clearRow(formModel[item.prop], scope)"
                            >清空</el-button
                          >
                        </el-dropdown-item>
                        <el-dropdown-item>
                          <el-button
                            v-if="
                              oc(
                                item,
                                'tableOption.operationColumn.deleteBtn'
                              ) !== false || false
                            "
                            :disabled="formModel[item.prop].length < 2"
                            type="text"
                            icon="el-icon-delete"
                            @click="
                              formModel[item.prop].splice(scope.$index, 1)
                            "
                            >删除</el-button
                          >
                        </el-dropdown-item>
                      </el-dropdown-menu>
                    </el-dropdown>
                    <div
                      v-else-if="
                        oc(item, 'tableOption.operationColumn.menuType') ===
                          'button' || true
                      "
                    >
                      <el-button
                        v-if="
                          oc(item, 'tableOption.operationColumn.upMoveBtn') !==
                          false
                        "
                        type="text"
                        @click="tableRowUpOne(formModel[item.prop], scope)"
                        :disabled="scope.$index === 0"
                      >
                        <i class="el-icon-top"></i>上移
                      </el-button>
                      <el-button
                        v-if="
                          oc(
                            item,
                            'tableOption.operationColumn.downMoveBtn'
                          ) !== false
                        "
                        type="text"
                        @click="tableRowDownOne(formModel[item.prop], scope)"
                        :disabled="
                          scope.$index === formModel[item.prop].length - 1
                        "
                      >
                        <i class="el-icon-bottom"></i>下移
                      </el-button>
                      <el-button
                        v-if="
                          oc(item, 'tableOption.operationColumn.clearBtn') !==
                            false || false
                        "
                        type="text"
                        icon="el-icon-close"
                        @click="clearRow(formModel[item.prop], scope)"
                        >清空</el-button
                      >
                      <el-button
                        v-if="
                          oc(item, 'tableOption.operationColumn.deleteBtn') !==
                            false || false
                        "
                        :disabled="formModel[item.prop].length < 2"
                        type="text"
                        icon="el-icon-delete"
                        @click="formModel[item.prop].splice(scope.$index, 1)"
                        >删除</el-button
                      >
                    </div>
                  </template>
                </el-table-column>
              </el-table>
              <!-- 表格批量表单的后续添加一行按钮 -->
              <div
                :style="{
                  textAlign:
                    oc(item, 'tableOption.addRowBtn.align') || 'center',
                }"
              >
                <el-button
                  :style="{ width: oc(item, 'tableOption.addRowBtn.width') }"
                  v-show="
                    oc(item, 'tableOption.addRowBtn.display') === false
                      ? false
                      : true
                  "
                  :type="oc(item, 'tableOption.addRowBtn.type') || 'primary'"
                  :size="oc(item, 'tableOption.addRowBtn.size') || 'small'"
                  :disabled="
                    oc(item, 'tableOption.addRowBtn.disabled') || false
                  "
                  @click="tableAddRow(formModel[item.prop], item)"
                  >{{
                    oc(item, "tableOption.addRowBtn.text") || "增加一行"
                  }}</el-button
                >
              </div>
            </el-form-item>
          </div>
        </transition>
      </el-col>

      <!-- 菜单操作col 提交，重置等按钮的 -->
      <el-col
        :style="{
          margin: '0 2px',
          clear: oc(option, 'menu.newRow') ? 'both' : 'none',
        }"
        v-if="c_menuDisplay"
        :md="oc(option, 'menu.span') || 24"
        :xs="24"
        :sm="12"
        :align="oc(option, 'menu.align') || 'center'"
      >
        <el-button
          :style="{
            marginLeft: oc(option, 'menu.align') === 'left' ? '20px' : '0',
            width: oc(option, 'menu.submitBtn.width') + 'px',
          }"
          :type="oc(option, 'menu.submitBtn.type') || 'primary'"
          :size="oc(option, 'menu.submitBtn.size') || 'small'"
          @click="submitbtn"
          :icon="
            loading
              ? 'el-icon-loading'
              : oc(option, 'menu.submitBtn.icon') || 'el-icon-circle-check'
          "
          :disabled="loading"
          >{{ oc(option, "menu.submitBtn.text") || "提交" }}</el-button
        >
        <el-button
          v-if="oc(option, 'menu.resetBtn.display') !== false"
          :type="oc(option, 'menu.resetBtn.type') || ''"
          :size="oc(option, 'menu.resetBtn.size') || 'small'"
          @click="resetbtn"
          :icon="oc(option, 'menu.resetBtn.icon') || 'el-icon-delete'"
          :disabled="oc(option, 'menu.resetBtn.disabled') || loading"
          >{{ oc(option, "menu.resetBtn.text") || "清空" }}</el-button
        >
        <slot name="menu"></slot>
      </el-col>
    </el-row>
    <!-- {{option.items}} -->
    <!-- {{formModel}} -->
  </el-form>
</template>
 <script>
import objectMerge from "../../utils/objectMerge";
import objectDeepClone from "../../utils/objectDeepClone";

const interfaceMixins = {
  methods: {
    // 调用提交按钮事件
    submit() {
      this.submitbtn();
    },
  },
};
export default {
  name: "base-form",
  props: {
    option: {
      type: Object,
      required: true,
      default() {
        return {
          labelWidth: "100px",
          gutter: 10,
          menu: {
            span: 12,
          },
          items: [
            {
              span: 4,
              label: "输入",
              prop: "val1",
              labelWidth: "60px",
              rules: [{ required: true, message: "必填项", trigger: "blur" }],
              value: 12,
              component: "el-input",
              componentProps: {
                type: "text",
                clearable: true,
                prefixIcon: "el-icon-document",
                placeholder: "内容",
                slots: ["suffix", "append"],
              },
            },
          ],
        };
      },
    },
    value: {
      type: Object,
      required: true,
    },
  },
  mixins: [interfaceMixins],
  data() {
    return {
      formModel: {},
      tableDatas: {},
      currentTableIndex: -1,
      loading: false,
    };
  },
  methods: {
    /* 对象可选链工具函数 */
    oc(obj, chain) {
      const chainList = chain.split(".");
      // console.log(chainList);
      chainList.some((item) => {
        // console.log(item);
        if (Object.prototype.toString.call(obj[item]) === "[object Boolean]") {
          obj = obj[item];
          return true;
        } else {
          obj = obj[item] || undefined;
          return !obj;
        }
      });
      return obj;
    },
    setLoading(val) {
      this.loading = !!val;
    },
    submitbtn() {
      const formData = objectMerge(
        this.formModel,
        this.getProps(),
        false,
        true,
        true,
        false
      );
      // console.log(formData);
      this.$refs.form.validate((valid) => {
        // console.log(valid);
        if (valid) {
          this.loading = true;
          this.$emit("submit", formData, this.setLoading);
        }
      });
    },
    resetbtn() {
      this.formModel = this.getProps();
      // this.$refs.form.resetFields();
      this.$emit("reset", this.formModel);
    },
    /* api,外部调用重置表单 */
    reset() {
      this.formModel = this.getProps();
    },
    onChange(formItem, e) {
      // this.$emit("input", this.formModel);
      // console.log(e);
      const a = {};
      a[formItem.prop] = e;
      // change 提供三个参数 ，整个form的对象值是v-model的，改变的prop及值 ，form item配置
      this.$emit("change", this.formModel, a, formItem);
    },
    // 根据v-model的数据，工厂函数创造表格的行数
    computeTableRows(item) {
      // console.log(rows);
      const props = item.columns.reduce((cur, next) => {
        cur[next.prop] = null;
        return cur;
      }, {});

      let valueLen = 0;
      if (this.value[item.prop]) {
        valueLen = this.value[item.prop].length;
      }
      // console.log(valueLen);
      if (valueLen > 1) {
        const a = [];
        for (let i = 0; i < valueLen; i++) {
          a.push(objectDeepClone(props));
        }
        return a;
      } else {
        return [props];
      }
    },
    tableListChange(scope) {
      // console.log(scope);
      this.$emit("change", this.formModel);
    },
    // 初始化时或者option变化时，获取所有的prop用来做成一个对象，提供数据使用
    initProp() {
      return this.option.items.reduce((obj, next) => {
        next.display === false ? "" : (obj[next.prop] = null);

        // obj[next.prop] = next.component === 'el-input-number'?0:'';
        if (next.table) {
          obj[next.prop] = this.computeTableRows(next);
        }
        return obj;
      }, {});
    },

    // 如果有options选项，并含有asyncData，则自动执行并通过回调把结果赋值给list
    getAsyncData() {
      this.option.items.forEach((item) => {
        if (
          item.options &&
          item.options.list.length < 1 &&
          item.options.asyncData
        ) {
          item.options.asyncData((data) => {
            item.options.list = data;
          });
        }
      });
    },
    getProps() {
      return this.initProp();
    },
    // 表格批量表单，增行
    tableAddRow(rows, item) {
      // console.log(rows);
      if (rows) {
        const item = objectDeepClone(rows[rows.length - 1], {}, true, true);
        rows.push(item);
      } else {
        // rows = this.computeTableRows(item);
      }
    },
    /* 表格批量表单，行操作，上移一行 */
    tableRowUpOne(tableList, scope) {
      const ind = scope.$index;
      const currentRow = tableList.splice(scope.$index, 1);
      // console.log(currentRow);
      tableList.splice(ind - 1, 0, currentRow[0]);
    },
    /* 表格批量表单，行操作，下移一行 */
    tableRowDownOne(tableList, scope) {
      const ind = scope.$index;
      const currentRow = tableList.splice(scope.$index, 1);
      // console.log(currentRow);
      tableList.splice(ind + 1, 0, currentRow[0]);
    },
    /* 表格，清空一行 */
    clearRow(tableData, scope) {
      Object.keys(tableData[scope.$index]).map((item) => {
        tableData[scope.$index][item] = null;
      });
    },
    /* 表格row click 暂未绑定*/
    tableRowClick(item, args) {
      // console.log(arguments);
      // const row = this.$utils.findObject(this.formModel[item.prop], args[0]);
      // this.currentTableIndex = row.index;
    },
    /* 移除表单的验证结果 */
    clearValidate() {
      this.$nextTick(() => {
        //  setTimeout(() => {
        this.$refs.form.clearValidate();
        // }, 200);
      });
    },
    /* 提供接口，获取ref */
    getRefs(ref) {
      // console.log(ref);
      // console.log(this.$refs);
      return this.$refs[ref][0];
    },
  },
  computed: {
    c_menuDisplay() {
      if (this.oc(this.option, "items")) {
        return (
          this.option.items.length > 0 &&
          this.oc(this.option, "menu.display") !== false
        );
      } else {
        return false;
      }
    },
  },
  components: {},
  watch: {
    value: {
      immediate: true,
      deep: true,
      handler(val) {
        // objectMerge(val,this.formModel,true)
        this.formModel = val;
        // 反合并策略，将option的prop提成model的对象，再向form的v-model添加属性，不覆盖已有值
        //objectMerge(this.initProp(),this.formModel,true,false,false)
      },
    },
    option: {
      deep: true,
      handler(val) {
        // this.initProp()
      },
    },
  },
  created() {
    this.getAsyncData();
    // this.initProp()
  },
  mounted() {
    // console.log(this.$listeners)
  },
};
</script>
 <style scoped>
/* 默认表格批量表单将每个组件width 100%了，需把switch调整回来 */
/deep/ .base-form-table .el-switch {
  width: auto !important;
}
/deep/ .el-col {
  min-height: 0px !important;
}

/* 解决checkbox group总是比其他item高了5px的问题，
找不到到底哪里高了5px。直接设置成这个，这样通过form的size还是可以设置大小 */
/deep/ .el-checkbox-group {
  height: 0px;
}

.base-form-not-error-msg >>> .el-form-item {
  margin-bottom: 6px !important;
}
/* TODO 修复label被加粗的问题 */
.base-form >>> .el-form-item__label {
  font-weight: normal;
}
</style>
